﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Graphics;
using Tesla.Math;

namespace Tesla.Core {

    /// <summary>
    /// A window is the part of the screen that a scene is rendered to. It is abstracted enough where a window can exist as a primary window (e.g.
    /// the main application window) or a secondary control that is added to another GUI application (e.g. an editor). Every window
    /// owns a swap chain that describes the back buffer.
    /// </summary>
    public class Window : IDisposable {
        private IWindowImplementation _impl;

        /// <summary>
        /// Adds or removes the event handler for when the window is resized.
        /// </summary>
        public event EventHandler ClientSizeChanged {
            add {
                _impl.ClientSizeChanged += value;
            }
            remove {
                _impl.ClientSizeChanged -= value;
            }
        }

        /// <summary>
        /// Adds or removes the event handler for when the window's orientation changes.
        /// </summary>
        public event EventHandler OrientationChanged {
            add {
                _impl.OrientationChanged += value;
            }
            remove {
                _impl.OrientationChanged -= value;
            }
        }

        /// <summary>
        /// Event for when the window is the primary window and has been activated.
        /// </summary>
        public event EventHandler Activated {
            add {
                _impl.Activated += value;
            }
            remove {
                _impl.Activated -= value;
            }
        }

        /// <summary>
        /// Event for when the window is the primary window and has been deactivated.
        /// </summary>
        public event EventHandler Deacativated {
            add {
                _impl.Deacativated += value;
            }
            remove {
                _impl.Deacativated -= value;
            }
        }

        /// <summary>
        /// Event for when the window is the primary window and has been resumed.
        /// </summary>
        public event EventHandler Resume {
            add {
                _impl.Resume += value;
            }
            remove {
                _impl.Resume -= value;
            }
        }

        /// <summary>
        /// Event for when the window is the primary window and has been suspended.
        /// </summary>
        public event EventHandler Suspend {
            add {
                _impl.Suspend += value;
            }
            remove {
                _impl.Suspend -= value;
            }
        }

        /// <summary>
        /// Gets the underlying window implementation.
        /// </summary>
        public IWindowImplementation Implementation {
            get {
                return _impl;
            }
        }

        /// <summary>
        /// Gets if the window is primary. Primary windows are top-level, e.g. a Form. Only primary windows can be used
        /// with a window host to run the application.
        /// </summary>
        public bool IsPrimaryWindow {
            get {
                return _impl.IsPrimaryWindow;
            }
        }

        /// <summary>
        /// Gets or sets if the user is allowed to change the size of the window.
        /// </summary>
        public bool AllowUserResizing {
            get {
                return _impl.AllowUserResizing;
            }
            set {
                _impl.AllowUserResizing = value;
            }
        }

        /// <summary>
        /// Gets the client bounds of the window.
        /// </summary>
        public Rectangle ClientBounds {
            get {
                return _impl.ClientBounds;
            }
        }

        /// <summary>
        /// Gets the handle to the underlying native window.
        /// </summary>
        public IntPtr Handle {
            get {
                return _impl.Handle;
            }
        }

        /// <summary>
        /// Gets if the window is minimized.
        /// </summary>
        public bool IsMinimized {
            get {
                return _impl.IsMinimized;
            }
        }

        /// <summary>
        /// Gets or sets if the mouse should be visible inside the window.
        /// </summary>
        public bool IsMouseVisible {
            get {
                return _impl.IsMouseVisible;
            }
            set {
                _impl.IsMouseVisible = value;
            }
        }

        /// <summary>
        /// Gets or sets the window's title.
        /// </summary>
        public String Title {
            get {
                return _impl.Title;
            }
            set {
                _impl.Title = value;
            }
        }

        /// <summary>
        /// Gets or sets if the window should be full screen. If the state changes, then the effect is toggled.
        /// </summary>
        public bool IsFullScreen {
            get {
                return _impl.IsFullScreen;
            }
            set {
                _impl.IsFullScreen = value;
            }
        }

        /// <summary>
        /// Gets the swap chain owned by the window.
        /// </summary>
        public SwapChain SwapChain {
            get {
                return _impl.SwapChain;
            }
        }

        /// <summary>
        /// Gets if the window is disposed.
        /// </summary>
        public bool IsDisposed {
            get {
                return _impl.IsDisposed;
            }
        }

        /// <summary>
        /// Creates a new window.
        /// </summary>
        /// <param name="pp">Presentation parameters for creating the swap chain.</param>
        public Window(PresentationParameters pp) {
            IWindowProvider provider = Engine.Services.GetService<IWindowProvider>();
            if(provider == null) {
                throw new TeslaException("Window provider cannot be null!");
            }
            _impl = provider.CreateWindowImplementation(pp, "Tesla Graphics Engine", true);
        }

        /// <summary>
        /// Creates a new window.
        /// </summary>
        /// <param name="pp">Presentation parameters for creating the swap chain.</param>
        /// <param name="isPrimaryWindow">True if the window is top-level, false if otherwise</param>
        public Window(PresentationParameters pp, bool isPrimaryWindow) {
            IWindowProvider provider = Engine.Services.GetService<IWindowProvider>();
            if(provider == null) {
                throw new TeslaException("Window provider cannot be null!");
            }
            _impl = provider.CreateWindowImplementation(pp, "Tesla Graphics Engine", isPrimaryWindow);
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="Window"/> is reclaimed by garbage collection.
        /// </summary>
        ~Window() {
            Dispose(false);
        }


        /// <summary>
        /// Resizes the client area of the window using the specified parameters.
        /// </summary>
        /// <param name="width">Width of the client area</param>
        /// <param name="height">Height of the client area</param>
        public void Resize(int width, int height) {
            _impl.Resize(width, height);
        }

        /// <summary>
        /// Dispose of the window.
        /// </summary>
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing) {
            if(_impl != null && !_impl.IsDisposed) {
                if(disposing) {
                    _impl.Dispose();
                }
            }
        }
    }
}
